/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
    constructor(executor) {
        try {
            executor(this.resolve, this.reject)
        } catch (error) {
            this.reject(error)
        }
    }

    status = PENDING;
    value = undefined;
    reason = undefined;

    // 成功回调
    successCallback = []
    // 失败回调
    failCallback = []

    resolve = (value) => {
        // 如果状态发生改变就不可再改
        if (this.status !== PENDING) return
        // 状态改变为成功
        this.status = FULFILLED
        this.value = value
        while (this.successCallback.length) {
            this.successCallback.shift()()
        }
    }

    reject = (reason) => {
        if (this.status !== PENDING) return
        // 状态改变为失败  
        this.status = REJECTED
        this.reason = reason;
        while (this.failCallback.length) {
            this.failCallback.shift()()
        }
    }

    then(successCallback, failCallback) {
        console.log('then')
        // 参数可选
        successCallback = successCallback ? successCallback : value => value;
        // 参数可选
        failCallback = failCallback ? failCallback : reason => { throw reason };
        return new MyPromise((resolve, reject) => {

            if (this.status === FULFILLED) {
                try {
                    let x = successCallback(this.value);
                    resolve(x)
                } catch (e) {
                    reject(x)
                }

            } else if (this.status === REJECTED) {
                try {
                    let x = failCallback(this.reason);
                    reject(x)
                } catch (e) {
                    reject(e);
                }
            } else {
                try {
                    this.successCallback.push(() => {
                        let x = successCallback(this.value);
                        if (x instanceof MyPromise) {
                            x.then(resolve, reject);
                        } else {
                            resolve(x)
                        }
                    })

                    this.failCallback.push(() => {
                        let x = failCallback(this.value);
                        if (x instanceof MyPromise) {
                            x.then(resolve, reject);
                        } else {
                            reject(x)
                        }

                    })
                } catch (e) {
                    reject(e);
                }
            }
        })
    }

    finally(callback) {
        return this.then(value => {
            console.log('finally', value)
            return MyPromise.resolve(callback()).then(() => value);
        }, reason => {
            return MyPromise.resolve(callback()).then(() => { throw reason })
        })
    }

    catch(callback) {
        return this.then(null, callback);
    }

    static resolve(value) {
        if (value instanceof MyPromise) {
            return value
        } else {
            return new MyPromise(resolve => resolve(value));
        }
    }

    static reject(value) {
        if (value instanceof MyPromise) {
            return value
        } else {
            return new MyPromise((resolve, reject) => { reject(value) });
        }
    }

    static all(promises) {
        let result = [];
        let index = 0;
        return new MyPromise((resolve, reject) => {
            promises.forEach((e, i) => {
                e.then(value => {
                    result[i] = value
                    index++
                    if (index === promises.length) {
                        resolve(result)
                    }
                }).catch(error => {
                    reject(error)
                })
            });
        })
    }

    static race(promises) {
        return new MyPromise((resolve, reject) => {
            promises.forEach(e => {
                e.then(value => {
                    resolve(value)
                }).catch(error => {
                    reject(error)
                })
            });
        })
    }

    static allSettled(promises) {
        let result = [];
        let index = 0;

        const pushResult = (value, status, i, resolve) => {
            index++;
            result[i] = {
                value,
                status
            }
            if (index === promises.length) {
                resolve(result)
            }
        }

        return new MyPromise((resolve, reject) => {
            promises.forEach((e, i) => {
                e.then(value => {
                    pushResult(value, 'fulfilled', i, resolve)
                }, error => {
                    pushResult(error, 'rejected', i, resolve)
                })
            });
        })
    }
}

const promise = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve('成功')
    }, 2000)
})


const promise2 = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        reject('失败')
    }, 2000)
})

promise.then(value => {
    console.log(value)
    return '链式调用'
}).catch(value => {
    console.log(value)
    return 'val'
}).finally(() => {
    console.log('finally')
}).then(value => {
    console.log(value)
})

MyPromise.allSettled([promise, promise2]).then(value => {
    console.log(value)
})

MyPromise.resolve('123').then(console.log)
MyPromise.reject('fail').then(null, console.log)

